﻿using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using Takkou.Language;

namespace Takkou.Core
{
    public class Robot : WorldElement
    {
        // robot's static image
        private static ImageSource _imgSrc = new BitmapImage(new Uri("Images/robot.png", UriKind.Relative));

        // robot's action plan and basic robot's actions
        public Block plan;
        public ActionCommand MOVE, TURN_LEFT, TURN_RIGHT;

        public Robot(string id, int x, int y, Dir dir)
            : base(id, ElmType.ROBOT)
        {
            View = new Sprite(_imgSrc)
            {
                X = x,
                Y = y,
                Direction = dir,
                Top = 9,
                Left = 13,
                /*
                CenterX = 25,
                CenterY = 20,
                 */
            };

            plan = new Block();
            MOVE = new ActionCommand(move);
            TURN_LEFT = new ActionCommand(turnLeft);
            TURN_RIGHT = new ActionCommand(turnRight);
        }

        public override void update()
        {
            plan.execute();
        }

        //--------------------------------------------------------
		// DEFAULT ROBOT ACTIONS
		//--------------------------------------------------------

		public bool move(params object[] args)
		{
            GameScreen gs = GameScreen.Instance;

            /* The grid cell width can be different from the cell height.
             * So, the move step should be in acccordance with. */
            if (View.Direction == Dir.NORTH || View.Direction == Dir.SOUTH)
            {
                return View.move(gs.CellHeight * gs.FrameRate, gs.CellHeight);
            }
            else
            {
                return View.move(gs.CellWidth * gs.FrameRate, gs.CellWidth);
            }
		}

        public bool turnLeft(params object[] args)
        {
            GameScreen gs = GameScreen.Instance;
            return View.TurnLeft(90 * gs.FrameRate);
        }

		public bool turnRight(params object[] args)
        {
            GameScreen gs = GameScreen.Instance;
            return View.TurnRight(90 * gs.FrameRate);
        }
    }

    //=========================================================================
    // Classes of robots.
    // Created for testing purposes
    //=========================================================================

    #region Basic robot with a Repeat test
    public class RepeatRobot : Robot
    {
        public RepeatRobot(string id, int x, int y, Dir dir)
            : base(id, x, y, dir)
        {
            /* Plan pseudo-code:
             *     repeat 2 times:
             *         move
             *         move
             *         turn_right
             *         turn_right
             *         move
             *         move
             */
            Block bl = new Block();
                bl.addStatement(MOVE, 2);
                bl.addStatement(TURN_RIGHT);
                bl.addStatement(MOVE, 2);
                bl.addStatement(TURN_LEFT, 2);
                bl.addStatement(MOVE, 2);
                bl.addStatement(TURN_LEFT);
                bl.addStatement(MOVE, 2);
                bl.addStatement(TURN_RIGHT, 2);
                Repeat rp = new Repeat(4, bl);
            plan.addStatement(rp);
        }
    }
    #endregion

    #region Basic robot with a RepeatUntil test
    public class RepeatUntilRobot : Robot
    {
        public RepeatUntilRobot(string id, int x, int y, Dir dir)
            : base(id, x, y, dir)
        {
            /* Plan pseudo-code:
             *     number count = 0
             *     repeat
             *         move
             *         move
             *         turn_right
             *         turn_right
             *         move
             *         move
             *         count = count + 1
             *     until count >= 4
             */

            // const values
            ValNumber zero = new ValNumber(0);
            ValNumber one = new ValNumber(1);
            ValNumber four = new ValNumber(4);

            // var declaration: count = 0
            NumberVar count = new NumberVar("count", zero);

            Block bl = new Block();
                bl.addStatement(MOVE, 2);
                bl.addStatement(TURN_RIGHT, 2);
                bl.addStatement(MOVE, 2);

                // assignment: count = count + 1
                BinaryExpNumber sum = new BinaryExpNumber(BinaryExpNumber.ExpType.ADD, count, one);
                bl.addStatement(new NumberAssignment(count, sum));

            // expression: count >= 4
            NumberLogicalExpression cond = new NumberLogicalExpression(NumberLogicalExpression.OperType.GREATER_OR_EQUALS, count, four);
            RepeatUntil ru = new RepeatUntil(bl, cond);

            plan.addStatement(ru);
        }
    }

    #endregion

    #region Miner class
    //public class Miner : Robot
    //{
    //    private const double MINING_VELOCITY = 10;   // velocity of mine action (minered units per second)
    //    private const double CAPACITY = 50;  // how many resources the miner is able to transport

    //    public double Transporting { get; private set; }

    //    public Miner(string id, int x, int y, Dir dir)
    //        : base(id, x, y, dir)
    //    {
    //        Resource rsc = (Resource) Game.World.getWorldElement("rsc03");
    //        Transporting = 0;

    //        /* Plan pseudo-code:
    //         *     move (9x)
    //         *     turn_left (2x)
    //         *     move (6x)
    //         *     repeat
    //         *         mine
    //         *     until miner.Transporting > miner.Capacity
    //         *     turn_right (4x)
    //         *     move (6x)
    //         *     turn_right (2x)
    //         *     move (9x)
    //         */
    //        Block bl = new Block();
    //            bl.addStatement(MOVE, 9);
    //            bl.addStatement(TURN_LEFT, 2);
    //            bl.addStatement(MOVE, 6);

    //            RefNumber quant = new RefNumber(() => this.Transporting);
    //            ValNumber capac = new ValNumber(CAPACITY);
    //            NumberLogicalExpression cond = new NumberLogicalExpression(NumberLogicalExpression.OperType.GREATER_OR_EQUALS, quant, capac);

    //            bl.addStatement(new RepeatUntil(MINE(rsc), cond));

    //            bl.addStatement(TURN_RIGHT, 4);
    //            bl.addStatement(MOVE, 6);
    //            bl.addStatement(TURN_RIGHT, 2);
    //            bl.addStatement(MOVE, 9);

    //        plan.addStatement(bl);
    //    }

    //    public ACommand MINE(Resource r)
    //    {
    //        return new ACommand(mine, r);
    //    }

    //    //--------------------------------------------------------
    //    // MINER ACTIONS
    //    //--------------------------------------------------------

    //    public bool mine(params object[] args)
    //    {
    //        if (args.Length != 1)
    //        {
    //            // TODO throw an exception: the mine action needs 1 param: the resource to mine
    //            // TODO the resource needs to be close to the miner
    //        }

    //        Resource rsc = (Resource)args[0]; // TODO check if the parameter has been passed

    //        if (!isCloseTo(rsc))
    //            return false;

    //        double quantity = MINING_VELOCITY * GameScreen.elapsedTime / SECOND;
    //        if (Transporting + quantity > CAPACITY)
    //        {
    //            quantity = CAPACITY - Transporting;
    //        }
    //        Transporting += rsc.pick(quantity);
    //        GameScreen.msgBar.Text = Transporting.ToString();
    //        return Transporting < CAPACITY;
    //    }
    //}
    #endregion




}
